package grdnlog.dao;

import grdnlog.bean.JpaBean;

import java.util.List;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;



public abstract class JpaDao<T extends JpaBean> {

	private EntityManager entityManager;

	@PersistenceContext
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	public EntityManager getEntityManager() {
		return entityManager;
	}

	public abstract Class<T> getEntityClass();
	
	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		final EntityManager entityManager = getEntityManager();
		final StringBuilder builder = new StringBuilder("SELECT o FROM ").append(getEntityClass().getName()).append(" o");
		
		final Query query = entityManager.createQuery(builder.toString());
		
	    return query.getResultList();
	}
	
	@SuppressWarnings("unchecked")
	public List<T> getAll(Map<String, Boolean> sortProperties) {
		final EntityManager entityManager = getEntityManager();
		final StringBuilder builder = new StringBuilder("SELECT o FROM ").append(getEntityClass().getName()).append(" o");
		
		if (sortProperties != null && !sortProperties.isEmpty()) {
			builder.append(" ORDER BY ");

			for (String property : sortProperties.keySet()) {
				
				builder.append(property);
			
				if (sortProperties.get(property)) {
					builder.append(" ASC,");
				} else {
					builder.append(" DESC,");
				}
			}
			
			builder.setLength(builder.length() - 1);
		}
		
		final Query query = entityManager.createQuery(builder.toString());
		
	    return query.getResultList();
	}

	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public void create(T customer) {
		final EntityManager entityManager = getEntityManager();
				
		entityManager.persist(customer);
		entityManager.flush();
	}
	
	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public void update(T customer) {
		final EntityManager entityManager = getEntityManager();
		
		entityManager.merge(customer);
		entityManager.flush();
	}

	@Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
	public void deleteById(String id) {
		final EntityManager entityManager = getEntityManager();

		final T customer = entityManager.find(getEntityClass(), id);
		
		entityManager.remove(customer);
		entityManager.flush();
	}
	
	public T getById(String id) {
		final EntityManager entityManager = getEntityManager();

		final T customer = entityManager.find(getEntityClass(), id);
		
		return customer; 
	}
	
	protected Query getPropertiesQuery(Map<String, Object> properties) {
		final EntityManager entityManager = getEntityManager();
		
		final StringBuilder builder = new StringBuilder("SELECT o FROM ").append(getEntityClass().getName()).append(" o");
		if (properties != null && properties.size() > 0) {
			builder.append(" WHERE");
			
			boolean first = true;
			int count = 1;
			
			for (String property : properties.keySet()) {
				if (!first){
					builder.append(" AND");
				}
				builder.append(" o.").append(property).append(" = ?").append(count);
				first = false;
				count++;
			}
		}
		
		final Query query = entityManager.createQuery(builder.toString());
		
		int count = 1;
		for (String property : properties.keySet()) {
			query.setParameter(count, properties.get(property));
			count++;
		}
		
		return query;
	}
	
	@SuppressWarnings("unchecked")
	public List<T> findByProperties(Map<String,Object> properties) {
		final Query query = getPropertiesQuery(properties);
		
		return query.getResultList();
	}
	
	@SuppressWarnings("unchecked")
	public T findUniqueByProperties(Map<String,Object> properties) {
		final Query query = getPropertiesQuery(properties);
		
		return (T) query.getSingleResult();
	}
	
	@SuppressWarnings("unchecked")
	public boolean isExistsByProperties(Map<String,Object> properties) {
		final Query query = getPropertiesQuery(properties);
		query.setMaxResults(1);
		
		final List<T> results = query.getResultList();
		
		return results != null && results.size() > 0;
	}
}
